﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;

namespace Noris.Tools.QCode.QrEncoder
{
    #region class QrEncoder - EncodeToFile()
    /// <summary>
    /// QR code encoder.
    /// More on: http://en.wikipedia.org/wiki/QR_code
    /// </summary>
    public partial class QrEncoder
    {
        #region EncodeToFile()
        /// <summary>
        /// Převede daný text do QR kódu a ten uloží jako obraz do daného souboru. Formát souboru odvodí z přípony zadaného jména souboru.
        /// Tato varianta používá korekci chyb High, velikost jednoho datového bodu = 8pixel, a 300dpi.
        /// </summary>
        /// <param name="content">Textový obsah kódu.</param>
        /// <param name="fileName">Jméno souboru. Pokud nebude adresář existovat, dojde k chybě. Pokud soubor existuje, bude přepsán.</param>
        public static void EncodeToFile(string content, string fileName)
        { _EncodeToFile(content, ErrorCorrectionLevel.High, null, 8, 300f, fileName); }
        /// <summary>
        /// Převede daný text do QR kódu a ten uloží jako obraz do daného souboru. Formát souboru odvodí z přípony zadaného jména souboru.
        /// Tato varianta používá velikost jednoho datového bodu = 8pixel, vytvoří obraz s rozlišením 300dpi.
        /// </summary>
        /// <param name="content">Textový obsah kódu.</param>
        /// <param name="errorCorrectionLevel">Úroveň zabezpečení před chybami.</param>
        /// <param name="fileName">Jméno souboru. Pokud nebude adresář existovat, dojde k chybě. Pokud soubor existuje, bude přepsán.</param>
        public static void EncodeToFile(string content, ErrorCorrectionLevel errorCorrectionLevel, string fileName)
        { _EncodeToFile(content, errorCorrectionLevel, null, 8, 300f, fileName); }

        /// <summary>
        /// Převede daný text do QR kódu a ten uloží jako obraz do daného souboru. Formát souboru odvodí z přípony zadaného jména souboru.
        /// Tato varianta používá korekci chyb High, QR kód umístí do dané velikosti obrazu, vytvoří obraz s rozlišením 300dpi.
        /// </summary>
        /// <param name="content">Textový obsah kódu.</param>
        /// <param name="size">Velikost výsledného obrazu v pixelech. QR kód bude umístěn na střed plochy. Velikost QR kódu je určena tak, aby co nejvíce využil danou plochu, ale nedošlo ke zkreslení (antialiasing).</param>
        /// <param name="fileName">Jméno souboru. Pokud nebude adresář existovat, dojde k chybě. Pokud soubor existuje, bude přepsán.</param>
        public static void EncodeToFile(string content, Size size, string fileName)
        { _EncodeToFile(content, ErrorCorrectionLevel.High, size, null, 300f, fileName); }
        /// <summary>
        /// Převede daný text do QR kódu a ten uloží jako obraz do daného souboru. Formát souboru odvodí z přípony zadaného jména souboru.
        /// Tato varianta používá korekci chyb High, QR kód umístí do dané velikosti obrazu.
        /// </summary>
        /// <param name="content">Textový obsah kódu.</param>
        /// <param name="size">Velikost výsledného obrazu v pixelech. QR kód bude umístěn na střed plochy. Velikost QR kódu je určena tak, aby co nejvíce využil danou plochu, ale nedošlo ke zkreslení (antialiasing).</param>
        /// <param name="resolutionDPI">Rozlišení. Nemá vliv na velikost obrazu.</param>
        /// <param name="fileName">Jméno souboru. Pokud nebude adresář existovat, dojde k chybě. Pokud soubor existuje, bude přepsán.</param>
        public static void EncodeToFile(string content, Size size, float resolutionDPI, string fileName)
        { _EncodeToFile(content, ErrorCorrectionLevel.High, size, null, resolutionDPI, fileName); }
        /// <summary>
        /// Převede daný text do QR kódu a ten uloží jako obraz do daného souboru. Formát souboru odvodí z přípony zadaného jména souboru.
        /// Tato varianta QR kód umístí do dané velikosti obrazu, vytvoří obraz s rozlišením 300dpi.
        /// </summary>
        /// <param name="content">Textový obsah kódu.</param>
        /// <param name="errorCorrectionLevel">Úroveň zabezpečení před chybami.</param>
        /// <param name="size">Velikost výsledného obrazu v pixelech. QR kód bude umístěn na střed plochy. Velikost QR kódu je určena tak, aby co nejvíce využil danou plochu, ale nedošlo ke zkreslení (antialiasing).</param>
        /// <param name="fileName">Jméno souboru. Pokud nebude adresář existovat, dojde k chybě. Pokud soubor existuje, bude přepsán.</param>
        public static void EncodeToFile(string content, ErrorCorrectionLevel errorCorrectionLevel, Size size, string fileName)
        { _EncodeToFile(content, errorCorrectionLevel, size, null, 300f, fileName); }
        /// <summary>
        /// Převede daný text do QR kódu a ten uloží jako obraz do daného souboru. Formát souboru odvodí z přípony zadaného jména souboru.
        /// Tato varianta QR kód umístí do dané velikosti obrazu.
        /// </summary>
        /// <param name="content">Textový obsah kódu.</param>
        /// <param name="errorCorrectionLevel">Úroveň zabezpečení před chybami.</param>
        /// <param name="size">Velikost výsledného obrazu v pixelech. QR kód bude umístěn na střed plochy. Velikost QR kódu je určena tak, aby co nejvíce využil danou plochu, ale nedošlo ke zkreslení (antialiasing).</param>
        /// <param name="resolutionDPI">Rozlišení. Nemá vliv na velikost obrazu.</param>
        /// <param name="fileName">Jméno souboru. Pokud nebude adresář existovat, dojde k chybě. Pokud soubor existuje, bude přepsán.</param>
        public static void EncodeToFile(string content, ErrorCorrectionLevel errorCorrectionLevel, Size size, float resolutionDPI, string fileName)
        { _EncodeToFile(content, errorCorrectionLevel, size, null, resolutionDPI, fileName); }

        /// <summary>
        /// Převede daný text do QR kódu a ten uloží jako obraz do daného souboru. Formát souboru odvodí z přípony zadaného jména souboru.
        /// Tato varianta používá korekci chyb High, velikost obrazu určí tak, aby jeden datový bod měl daný počet pixelů, vytvoří obraz s rozlišením 300dpi.
        /// </summary>
        /// <param name="content">Textový obsah kódu.</param>
        /// <param name="moduleSize">Počet pixelů na jeden logický bod kódu. Velikost obrazu bude určena na základě této velikosti a počtu bodů kódu.</param>
        /// <param name="fileName">Jméno souboru. Pokud nebude adresář existovat, dojde k chybě. Pokud soubor existuje, bude přepsán.</param>
        public static void EncodeToFile(string content, int moduleSize, string fileName)
        { _EncodeToFile(content, ErrorCorrectionLevel.High, null, moduleSize, 300f, fileName); }
        /// <summary>
        /// Převede daný text do QR kódu a ten uloží jako obraz do daného souboru. Formát souboru odvodí z přípony zadaného jména souboru.
        /// Tato varianta používá korekci chyb High, velikost obrazu určí tak, aby jeden datový bod měl daný počet pixelů.
        /// </summary>
        /// <param name="content">Textový obsah kódu.</param>
        /// <param name="moduleSize">Počet pixelů na jeden logický bod kódu. Velikost obrazu bude určena na základě této velikosti a počtu bodů kódu.</param>
        /// <param name="resolutionDPI">Rozlišení. Nemá vliv na velikost obrazu.</param>
        /// <param name="fileName">Jméno souboru. Pokud nebude adresář existovat, dojde k chybě. Pokud soubor existuje, bude přepsán.</param>
        public static void EncodeToFile(string content, int moduleSize, float resolutionDPI, string fileName)
        { _EncodeToFile(content, ErrorCorrectionLevel.High, null, moduleSize, resolutionDPI, fileName); }
        /// <summary>
        /// Převede daný text do QR kódu a ten uloží jako obraz do daného souboru. Formát souboru odvodí z přípony zadaného jména souboru.
        /// Tato varianta velikost obrazu určí tak, aby jeden datový bod měl daný počet pixelů, vytvoří obraz s rozlišením 300dpi.
        /// </summary>
        /// <param name="content">Textový obsah kódu.</param>
        /// <param name="errorCorrectionLevel">Úroveň zabezpečení před chybami.</param>
        /// <param name="moduleSize">Počet pixelů na jeden logický bod kódu. Velikost obrazu bude určena na základě této velikosti a počtu bodů kódu.</param>
        /// <param name="fileName">Jméno souboru. Pokud nebude adresář existovat, dojde k chybě. Pokud soubor existuje, bude přepsán.</param>
        public static void EncodeToFile(string content, ErrorCorrectionLevel errorCorrectionLevel, int moduleSize, string fileName)
        { _EncodeToFile(content, errorCorrectionLevel, null, moduleSize, 300f, fileName); }
        /// <summary>
        /// Převede daný text do QR kódu a ten uloží jako obraz do daného souboru. Formát souboru odvodí z přípony zadaného jména souboru.
        /// Tato varianta velikost obrazu určí tak, aby jeden datový bod měl daný počet pixelů.
        /// </summary>
        /// <param name="content">Textový obsah kódu.</param>
        /// <param name="errorCorrectionLevel">Úroveň zabezpečení před chybami.</param>
        /// <param name="moduleSize">Počet pixelů na jeden logický bod kódu. Velikost obrazu bude určena na základě této velikosti a počtu bodů kódu.</param>
        /// <param name="resolutionDPI">Rozlišení. Nemá vliv na velikost obrazu.</param>
        /// <param name="fileName">Jméno souboru. Pokud nebude adresář existovat, dojde k chybě. Pokud soubor existuje, bude přepsán.</param>
        public static void EncodeToFile(string content, ErrorCorrectionLevel errorCorrectionLevel, int moduleSize, float resolutionDPI, string fileName)
        { _EncodeToFile(content, errorCorrectionLevel, null, moduleSize, resolutionDPI, fileName); }

        private static void _EncodeToFile(string content, ErrorCorrectionLevel errorCorrectionLevel, Size? size, int? moduleSize, float resolutionDPI, string fileName)
        {
            QrCode qrCode = _Encode(content, errorCorrectionLevel, null);
            System.Drawing.Imaging.ImageFormat format = BitMatrix.GetImageFormatFromName(fileName);
            Bitmap bitmap = null;
            if (moduleSize.HasValue)
                bitmap = qrCode.Matrix.ToBitmap(moduleSize.Value, resolutionDPI);
            else if (size.HasValue)
                bitmap = qrCode.Matrix.ToBitmap(size.Value, resolutionDPI);
            bitmap.Save(fileName, format);
        }
        #endregion
    }
    #endregion
    #region class BitMatrix - PaintTo(), ToBitmap()
    /// <summary>
    /// Class containing bit matrix of QR code
    /// </summary>
    public partial class BitMatrix
    {
        #region PaintTo() : vykreslí matrix do grafiky
        /// <summary>
        /// Vykreslí daný kód skrz danou grafiku do uvedeného prostoru, v daných barvách.
        /// Vrací true = OK / false = chyba (typicky malý prostor).
        /// </summary>
        /// <param name="graphics"></param>
        /// <param name="area"></param>
        /// <param name="colorLight"></param>
        /// <param name="colorDark"></param>
        /// <returns></returns>
        public bool PaintCodeTo(Graphics graphics, Rectangle area, Color colorLight, Color colorDark)
        {
            // Výpočet velikosti jednoho bodu:
            int moduleWidth = 0;
            Size clientSize = area.Size;
            int minDim = (clientSize.Width < clientSize.Height ? clientSize.Width : clientSize.Height);
            if (minDim <= 0) return false;
            int matrixSize = this.Width;
            moduleWidth = minDim / matrixSize;
            if (moduleWidth <= 0) return false;

            // Určení prostoru, který reálně obsadí kód:
            int codeDim = moduleWidth * matrixSize;
            int x = (clientSize.Width - codeDim) / 2;
            int y = (clientSize.Height - codeDim) / 2;
            Rectangle codeArea = new Rectangle(area.X + x, area.Y + y, codeDim, codeDim);

            // Kreslení:
            using (SolidBrush brush = new SolidBrush(colorLight))
            {
                if (!colorLight.IsEmpty)
                    graphics.FillRectangle(brush, codeArea);

                brush.Color = colorDark;

                int i = 0;  // Logická dimenze ve směru X
                int j = 0;  // Logická dimenze ve směru Y
                int fd = codeArea.Width;                             // Fyzický rozměr. Šířka i výška jsou shodné.
                for (int fy = 0; fy < fd; fy += moduleWidth)         // Souřadnice Y jde odshora, v krocích podle velikosti "bodu" (mw)
                {
                    i = 0;
                    for (int fx = 0; fx < fd; fx += moduleWidth)
                    {
                        if (this[i, j])
                        {   // Kreslit budu jen černé body:
                            Rectangle point = new Rectangle(codeArea.X + fx, codeArea.Y + fy, moduleWidth, moduleWidth);
                            graphics.FillRectangle(brush, point);
                        }
                        i++;
                    }
                    j++;
                }
            }
            return true;
        }
        #endregion
        #region Export QR kódu do bitmapy
        /// <summary>
        /// Vrátí obrázek obsahující tento QR kód, vykreslený v dané velikosti bitmapy.
        /// </summary>
        /// <param name="size">Velikost bitmapy v pixelech. Namísto velikosti je možno zadat počet pixelů na jeden datový bod (čtvereček).</param>
        /// <returns></returns>
        public Bitmap ToBitmap(Size size)
        {
            return this._ToBitmap(size, 300f, Color.LightGray, Color.White, Color.Black);
        }
        /// <summary>
        /// Vrátí obrázek obsahující tento QR kód, vykreslený v dané velikosti bitmapy a s daným rozlišením (jde jen o údaj o rozlišení, nemá vliv na skutečnou velikost čtverečku kódu).
        /// </summary>
        /// <param name="size">Velikost bitmapy v pixelech. Namísto velikosti je možno zadat počet pixelů na jeden datový bod (čtvereček).</param>
        /// <param name="resolutionDPI">Rozlišení bitmapy, určuje fyzickou velikost v mm (kdežto zadaná velikost size je v pixelech)</param>
        /// <returns></returns>
        public Bitmap ToBitmap(Size size, float resolutionDPI)
        {
            return this._ToBitmap(size, resolutionDPI, Color.LightGray, Color.White, Color.Black);
        }
        /// <summary>
        /// Vrátí obrázek obsahující tento QR kód, vykreslený v dané velikosti bitmapy a s daným rozlišením (jde jen o údaj o rozlišení, nemá vliv na skutečnou velikost čtverečku kódu).
        /// Lze zadat barvy okolí kódu (běžně šedá), barvu podkladu (běžně bílá) a barvu kódu (běžně černá).
        /// </summary>
        /// <param name="size">Velikost bitmapy v pixelech. Namísto velikosti je možno zadat počet pixelů na jeden datový bod (čtvereček).</param>
        /// <param name="resolutionDPI">Rozlišení bitmapy, určuje fyzickou velikost v mm (kdežto zadaná velikost size je v pixelech)</param>
        /// <param name="grayColor">Barva okolí kódu, výchozí je Color.LightGray</param>
        /// <param name="whiteColor">Barva pozadí kódu, výchozí je Color.White</param>
        /// <param name="blackColor">Barva kódu, výchozí je Color.Black</param>
        /// <returns></returns>
        public Bitmap ToBitmap(Size size, float resolutionDPI, Color grayColor, Color whiteColor, Color blackColor)
        {
            return this._ToBitmap(size, resolutionDPI, grayColor, whiteColor, blackColor);
        }
        /// <summary>
        /// Vrátí obrázek obsahující tento QR kód, vykreslený v dané velikosti bitmapy.
        /// </summary>
        /// <param name="moduleSize">Počet grafických pixelů na jeden datový bod (čtvereček). Alternativně je možno zadat cílovou velikost bitmapy, do které se kód umístí.</param>
        /// <returns></returns>
        public Bitmap ToBitmap(int moduleSize)
        {
            return this._ToBitmap(moduleSize, 300f, Color.LightGray, Color.White, Color.Black);
        }
        /// <summary>
        /// Vrátí obrázek obsahující tento QR kód, vykreslený v dané velikosti bitmapy a s daným rozlišením (jde jen o údaj o rozlišení, nemá vliv na skutečnou velikost čtverečku kódu).
        /// </summary>
        /// <param name="moduleSize">Počet grafických pixelů na jeden datový bod (čtvereček). Alternativně je možno zadat cílovou velikost bitmapy, do které se kód umístí.</param>
        /// <param name="resolutionDPI">Rozlišení bitmapy, určuje fyzickou velikost v mm (kdežto zadaná velikost size je v pixelech)</param>
        /// <returns></returns>
        public Bitmap ToBitmap(int moduleSize, float resolutionDPI)
        {
            return this._ToBitmap(moduleSize, resolutionDPI, Color.LightGray, Color.White, Color.Black);
        }
        /// <summary>
        /// Vrátí obrázek obsahující tento QR kód, vykreslený v dané velikosti bitmapy a s daným rozlišením (jde jen o údaj o rozlišení, nemá vliv na skutečnou velikost čtverečku kódu).
        /// Lze zadat barvy okolí kódu (běžně šedá), barvu podkladu (běžně bílá) a barvu kódu (běžně černá).
        /// </summary>
        /// <param name="moduleSize">Počet grafických pixelů na jeden datový bod (čtvereček). Alternativně je možno zadat cílovou velikost bitmapy, do které se kód umístí.</param>
        /// <param name="resolutionDPI">Rozlišení bitmapy, určuje fyzickou velikost v mm (kdežto zadaná velikost size je v pixelech)</param>
        /// <param name="grayColor">Barva okolí kódu, výchozí je Color.LightGray</param>
        /// <param name="whiteColor">Barva pozadí kódu, výchozí je Color.White</param>
        /// <param name="blackColor">Barva kódu, výchozí je Color.Black</param>
        /// <returns></returns>
        public Bitmap ToBitmap(int moduleSize, float resolutionDPI, Color grayColor, Color whiteColor, Color blackColor)
        {
            return this._ToBitmap(moduleSize, resolutionDPI, grayColor, whiteColor, blackColor);
        }
        private Bitmap _ToBitmap(int moduleSize, float resolutionDPI, Color grayColor, Color whiteColor, Color blackColor)
        {
            Size size = this._GetSizeForModuleSize(moduleSize);
            return this._ToBitmap(size, resolutionDPI, grayColor, whiteColor, blackColor);
        }
        private Bitmap _ToBitmap(Size size, float resolutionDPI, Color grayColor, Color whiteColor, Color blackColor)
        {
            Bitmap bmp = new Bitmap(size.Width, size.Height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
            bmp.SetResolution(resolutionDPI, resolutionDPI);

            Rectangle area = new Rectangle(0, 0, size.Width, size.Height);
            using (Graphics graphics = Graphics.FromImage(bmp))
            {
                graphics.Clear(grayColor);
                this.PaintCodeTo(graphics, area, whiteColor, blackColor);
            }

            return bmp;
        }
        /// <summary>
        /// Vrátí velikost obrázku (Size v pixelech), kterou musí mít, aby aktuální kód vykreslil s danou velikostí jednoho QR bodu (počet pixelů na čtvereček)
        /// </summary>
        /// <param name="moduleSize">Velikost jednoho QR bodu (počet pixelů na čtvereček)</param>
        /// <returns></returns>
        private Size _GetSizeForModuleSize(int moduleSize)
        {
            int codeDim = this.Width * moduleSize;
            return new Size(codeDim, codeDim);
        }
        /// <summary>
        /// Vrátí System.Drawing.Imaging.ImageFormat odpovídající příponě jména souboru.
        /// Pracuje pouze pro přípony BMP, FIG, JPG, JPEG, PNG, TIF, TIFF. Implicitní je GIF.
        /// Ostatní formáty nevrací, protože nejsou kompatibilní s kreslením pixelů pomocí třídy Bitmap.
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public static System.Drawing.Imaging.ImageFormat GetImageFormatFromName(string name)
        {
            string ext = System.IO.Path.GetExtension(name).ToLower();
            switch (ext)
            {
                case ".bmp": return System.Drawing.Imaging.ImageFormat.Bmp;
                // case ".emf": return System.Drawing.Imaging.ImageFormat.Emf;
                // case ".exif": return System.Drawing.Imaging.ImageFormat.Exif;
                case ".gif": return System.Drawing.Imaging.ImageFormat.Gif;
                // case ".ico": return System.Drawing.Imaging.ImageFormat.Icon;
                case ".jpg": return System.Drawing.Imaging.ImageFormat.Jpeg;
                case ".jpeg": return System.Drawing.Imaging.ImageFormat.Jpeg;
                case ".png": return System.Drawing.Imaging.ImageFormat.Png;
                case ".tif": return System.Drawing.Imaging.ImageFormat.Tiff;
                case ".tiff": return System.Drawing.Imaging.ImageFormat.Tiff;
                // case ".wmf": return System.Drawing.Imaging.ImageFormat.Wmf;
            }
            return System.Drawing.Imaging.ImageFormat.Gif;
        }
        #endregion
    }
    #endregion
}
namespace Noris.Tools.QCode.QEncoder
{
}
